#pragma once
#include <stdint.h>
#include <string>

class SHA1;
namespace helper {

template <typename T>
T IntegralChangeByteOrder(T value) {
    uint8_t buf[sizeof(T)] = {0};
    uint8_t *ptr = reinterpret_cast<uint8_t *>(&value);
    for (size_t i = 0; i < sizeof(T); i++) {
        buf[i] = ptr[sizeof(T) - i - 1];
    }
    return *reinterpret_cast<T *>(buf);
}

static inline bool IsLittleEndian() {
    static int value = 1;
    return *reinterpret_cast<char *>(&value) == 1;
}

class SHA256Impl;
class SHA256 {
public:
    static const size_t OUTPUT_SIZE = 32;
    SHA256();
    ~SHA256();
    SHA256 &Write(const unsigned char *data, size_t len);
    inline SHA256 &Write(const char *data, size_t len) {
        return this->Write(reinterpret_cast<const unsigned char *>(data), len);
    }

    void Finalize(unsigned char hash[OUTPUT_SIZE]);
    std::string ReadableResult();

    SHA256 &Reset();

private:
    SHA256Impl *_impl;
};

class SHA1 {
public:
    static const size_t OUTPUT_SIZE = 20;

    SHA1(/* args */);
    ~SHA1();

    SHA1 &Write(const unsigned char *data, size_t len);
    inline SHA1 &Write(const char *data, size_t len) {
        return this->Write(reinterpret_cast<const unsigned char *>(data), len);
    }

    bool Finalize(unsigned char hash[OUTPUT_SIZE]);
    std::string ReadableResult();

    SHA1 &Reset();

private:
    ::SHA1 *_impl;
};

}  // namespace helper
